<?php

/**
 * 命令行执行，WebSocket 服务
 * @noinspection ForgottenDebugOutputInspection
 */

require './comm.php';

use Workerman\Worker;
use Workerman\Lib\Timer;
use Workerman\Events\EventInterface;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Response;
use Workerman\Http\Client as HttpClient;
use Workerman\Redis\Client as RedisClient;
use Workerman\Connection\AsyncTcpConnection;

//心跳间隔 x 秒
const HEARTBEAT_TIME = 10;

//监听地址:端口
const LISTEN_URL = 'websocket://0.0.0.0:9000';

Worker::$logFile = ROOT_PATH.'runtime/log/websocket.log';
Worker::$pidFile = ROOT_PATH.'runtime/pid_websocket.log';

$wsContext = [
    //更多SSL选项请参考手册：http://php.net/manual/zh/context.ssl.php
    'ssl' => [
        //请使用绝对路径
        //'local_cert'    => ROOT_PATH.'cert/server.pem', // 也可以是 crt文件
        //'local_pk'      => ROOT_PATH.'cert/server.key',
        //'verify_peer'   => false,
        //'allow_self_signed' => true, //如果是自签名证书需要开启此选项
    ]
];

$wsWorker = new Worker(LISTEN_URL, $wsContext);

if (!empty($wsContext['ssl']['local_cert'])) {
    $wsWorker->transport = 'ssl'; //PHP必须安装OpenSSL扩展。
}

//启动2个进程 对外提供服务
$wsWorker->count = 2;

$wsWorker->name = 'MyWebSocket';

//绑定用户与连接
$wsWorker->uidConnections = []; // [uid => connection]

//worker子进程 启动时的回调函数
$wsWorker->onWorkerStart = function($wsWorker)
{
    echo PHP_EOL. "#$wsWorker->id 进程已启动" .PHP_EOL;

	global $db, $redis; //在此定义数连接

    //心跳检测
	Timer::add(HEARTBEAT_TIME, function() use ($wsWorker)
    {
        static $count = 0;
		if ($wsWorker->connections) {
            $count++;
			$nowTime = time();
			foreach ($wsWorker->connections as $connId => $connection) {
				if (!isset($connection->lastTime)) {
					$connection->lastTime = $nowTime;
				}
				if ($nowTime - $connection->lastTime > HEARTBEAT_TIME + 3) {
					$connection->close();
					continue;
				}
				//$connection->send('{"action": "ping", "count": "' . $count . '"}');
			}
		}
		else {
            echo PHP_EOL. "没有客户端连接" .PHP_EOL;
        }
	});
};


$wsWorker->onConnect = function($connection)
{
    $connId = $connection->id;
    $connection->lastTime = time();

    echo PHP_EOL. "客户端 #$connId 上线，IP：" . $connection->getRemoteIp() .PHP_EOL;

    $connection->send('{"action": "connect", "data": "' . $connId . '"}');

	$connection->onWebSocketConnect = function ($connection, $httpHeader) {
		//可以在这里判断连接来源是否合法，不合法就关掉连接
	};

    foreach($connection->worker->connections as $id => $conn)
    {
        //$conn->send('{"action": "notice", "data": "客户端 #' . $connId . ' 上线."}');
    }
};


$wsWorker->onMessage = function($connection, $data) use ($wsWorker)
{
    $connId = $connection->id;
    $connection->lastTime = time();

    echo PHP_EOL. '服务端收到 #' .$connId. ' 的消息：' .PHP_EOL. $data .PHP_EOL;

    if ($data === 'pong') {
        return;
    }
    elseif ($data === 'ping') {
        $connection->send('pong');
        return;
    }

    $arrayData = $data ? json_decode($data, true) : [];

    if (empty($arrayData) || empty($arrayData['action'])) {
        $connection->send('服务端收到 #' .$connId. ' 的消息：' . $data);
        return;
    };

    switch ($arrayData['action']) {

        case 'pong':
            return;

        case 'ping':
            $connection->send('{"action": "pong", "count": "' . ($arrayData['count'] ?? 0) . '"}');
            return;

        case 'bindConnection': //绑定用户与连接
            if(!empty($arrayData['uid'])) {
                $connection->uid = $arrayData['uid'];
                $wsWorker->uidConnections[$connection->uid] = $connection;
            }
            return;

        default:
            //todo...
            return;
    }
};


$wsWorker->onError = function($connection, $code, $message)
{
    echo PHP_EOL. "error: [$code] $message" .PHP_EOL;
    $connection->errorCode = $code;
    $connection->close();
};

$wsWorker->onClose = function($connection) use ($wsWorker)
{
    echo PHP_EOL. '客户端 #' . $connection->id .' 断开'. PHP_EOL;

    if(isset($connection->uid)) {
        unset($wsWorker->uidConnections[$connection->uid]);
    }

    if (isset($connection->timerId)) {
        Timer::del($connection->timerId);
    }
};

Worker::runAll();
